package helper.scxml.scxml2.t0.case1_cycle.test

import helper.base.A2LHM
import helper.base.BaseTypeHelper.LHMGetExpand.getMaxKey
import helper.base.BaseTypeHelper.LHMGetExpand.getMinKey
import helper.base.BaseTypeHelper.ListExpand.getMinValueKey
import helper.base.BaseTypeHelper.ListExpand.toArrayList
import helper.base.LHMHelper.A2LHMExpand.addIfAbsent
import helper.base.LHMHelper.A2LHMExpand.toStr
import helper.base.RandomHelper.Expand.getRandomE
import helper.base.RandomHelper.ifInRangeAndInP
import helper.base.TimeHelper
import helper.base.debug.DebugHelper.DebuggerList
import helper.base.debug.DebugHelper.DebuggerList.Companion.makeDebuggerList
import helper.base.debug.DebugHelper.LevelDebugger.Companion.debugger_2
import helper.base.debug.DebugHelper.LevelDebugger.Companion.getDefaultLevelDebugger
import helper.base.list.DoubleList
import helper.chart.ChartHelper.LineChartHelper.drawLinesChart
import helper.scxml.scxml2.EnvHelper.Expand.toLocationEventVListLHM
import helper.scxml.scxml2.EnvHelper.RunResult
import helper.scxml.scxml2.MathHelper.DSSUnit
import helper.scxml.scxml2.MathHelper.LHMToExpand.toDoubleArrayList
import helper.scxml.scxml2.Res
import helper.scxml.scxml2.SCXMLTuple
import helper.scxml.scxml2.StrategyTripleHelper.IRenEventSelector
import helper.scxml.scxml2.t0.case1_cycle.EnvHelper.Expand.toCostList
import helper.scxml.scxml2.t0.case1_cycle.EnvObjHelper
import helper.scxml.scxml2.t0.case1_cycle.LearningArgHelper.HyperArgUnit
import helper.scxml.scxml2.t0.case1_cycle.LearningArgHelper.HyperArgUnit.Companion.getDebugHyperArgUnit
import helper.scxml.scxml2.t0.case1_cycle.LearningArgHelper.HyperArgUnit.Companion.getType1HyperArgUnit
import org.junit.Test
import res.FileRes

typealias IRenEventSelectorFun = (SCXMLTuple) -> IRenEventSelector
typealias IRenEventSelectorFunCostListLHM = A2LHM<IRenEventSelectorFun, ArrayList<Double>>
typealias IRenEventSelectorFunMeanCostLHM = A2LHM<IRenEventSelectorFun, Double>

internal class LearningTest {
    class InstanceArgUnit(
        var nowCountOfNoBetter: Int = 0,
        var nowCountOfReset: Int = 0,
        var heap: ArrayList<RunResult> = ArrayList(),
        val meanList: ArrayList<Double> = ArrayList(),
        var lastMinCost: Double? = null,
        var indexAfterReset: Int = 0,
    ) {
        companion object {
            fun getObj1(): InstanceArgUnit {
                return InstanceArgUnit()
            }
        }

        fun resetLastMinCost() {
            lastMinCost = null
        }
    }

    val debugger_renEventSelectorCostListLHM = getDefaultLevelDebugger()
    val debugger_model_reset = getDefaultLevelDebugger()
    val debugger_model_iterations = getDefaultLevelDebugger()
    val debugger_before_compare = getDefaultLevelDebugger()
    val debugger_lastTest = getDefaultLevelDebugger()
    val debugger_afterIterations_printCostMeanList = getDefaultLevelDebugger()

    fun t1(
        hAU: HyperArgUnit,
        getDFun: DSSUnit.(DoubleList) -> Double,
        debuggerList: DebuggerList,
    ) {
        val nowTimeStr = TimeHelper.getT4NowTimeString()
        val ifDetermining = true
        val iAU = InstanceArgUnit.getObj1()
        val env = EnvObjHelper.getEnvObj1()

        var renEventSelectorCostListLHM = IRenEventSelectorFunCostListLHM()
        val renEventSelectorCostListLHMList = ArrayList<IRenEventSelectorFunCostListLHM>()
        var renEventSelectorCostMeanLHM = IRenEventSelectorFunMeanCostLHM()
        val renEventSelectorCostMeanLHMList = ArrayList<IRenEventSelectorFunMeanCostLHM>()
        renEventSelectorCostListLHMList.add(renEventSelectorCostListLHM)
        renEventSelectorCostMeanLHMList.add(renEventSelectorCostMeanLHM)

        debuggerList.pln(
            "iterations_start:",
            debugger_model_iterations,
        )
        repeat(
            hAU.maxIterations
        ) iteration@{
            if (iAU.nowCountOfReset >= hAU.maxResets) return@iteration
            debuggerList.pln(
                "indexOfIterations=${it},indexAfterReset=${iAU.indexAfterReset}",
                debugger_model_iterations,
            )
            iAU.indexAfterReset += 1
            val rrs = ArrayList<RunResult>()

            env.repeatRun2AndRecord(
                times = hAU.maxRuns,
                lhm = renEventSelectorCostListLHM,
                runResultList = rrs,
                debuggerList = debuggerList,
            )
            debuggerList.pln(
                """
                    after run
                    ${renEventSelectorCostListLHM.toStr()}
                """.trimIndent(),
                debugger_renEventSelectorCostListLHM,
            )
            val mean1 = renEventSelectorCostListLHM[env.strategyTuple.getRenEventSelectorFun]!!.average()
            iAU.meanList.add(mean1)
            val sorted: List<RunResult> = rrs.sortedBy {
                it.endData["c"]!!.toInt()
            }.take(hAU.maxGood)
            sorted.map {
                iAU.heap.add(it)
            }
            iAU.heap = iAU.heap.sortedBy {
                it.endData["c"]!!.toInt()
            }.take(hAU.maxBest).toArrayList()

            val locationEventVListLHM = iAU.heap.toLocationEventVListLHM(
                listOf(
                    Res.globalTimeId,
                    "x",
                ),
            )
            val oldGetIRenEventSelectorFun = env.strategyTuple.getRenEventSelectorFun
            env.strategyTuple.getRenEventSelectorFun = { scxmlTuple ->
                object : IRenEventSelector {
                    val dataXInt = scxmlTuple.dataSCXML.getDataInt("x")!!
                    override fun getEvent(
                        stateId: String
                    ): String? {
                        when (stateId) {
                            "s1" -> {
                                when (dataXInt) {
                                    100 -> {
                                        return "s1s4t1"
                                    }

                                    else -> {
                                        //为了均匀
                                        if (!ifInRangeAndInP(
                                                dataXInt,
                                                0 until 100,
                                            )
                                        ) return null
                                        val nowV = scxmlTuple.toData(
                                        ).toDoubleArrayList(
                                            arrayListOf(
                                                Res.globalTimeId,
                                                "x",
                                            )
                                        )
                                        val lhm = A2LHM<String, Double>()
                                        locationEventVListLHM[stateId]!!.map { (event, v) ->
                                            val d = v.getWeightOf(nowV, getDFun)
                                            lhm.addIfAbsent(
                                                event,
                                                d,
                                            )
                                        }
                                        return if (ifDetermining) {
                                            lhm.getMaxKey()
                                        } else {
                                            lhm.getRandomE()
                                        }
                                    }
                                }
                            }
                        }
                        return null
                    }
                }
            }

            env.repeatRun2AndRecord(
                times = hAU.evalRuns,
                lhm = renEventSelectorCostListLHM,
                debuggerList = debuggerList,
            )

            debuggerList.pln(
                """
                    after eval
                    ${renEventSelectorCostListLHM.toStr()}
                """.trimIndent(),
                debugger_renEventSelectorCostListLHM,
            )

            val mean2 = renEventSelectorCostListLHM[env.strategyTuple.getRenEventSelectorFun]!!.average()
            renEventSelectorCostMeanLHM[env.strategyTuple.getRenEventSelectorFun] = mean2

            debuggerList.pln(
                """
                before compare
                    mean2=${mean2}
                    iAU.lastMinCost=${iAU.lastMinCost}
                """.trimIndent(),
                debugger_before_compare,
            )

            if (iAU.lastMinCost == null || mean2 < iAU.lastMinCost!!) {
                iAU.nowCountOfNoBetter = 0
                iAU.lastMinCost = mean2
            } else {
                env.strategyTuple.getRenEventSelectorFun = oldGetIRenEventSelectorFun
                iAU.nowCountOfNoBetter += 1
                if (iAU.nowCountOfNoBetter >= hAU.maxNoBetter) {
                    //重置
                    debuggerList.pln(
                        "重置",
                        debugger_model_reset,
                    )
                    iAU.indexAfterReset = 0
                    renEventSelectorCostListLHM = LinkedHashMap()
                    renEventSelectorCostMeanLHM = LinkedHashMap()
                    renEventSelectorCostListLHMList.add(renEventSelectorCostListLHM)
                    renEventSelectorCostMeanLHMList.add(renEventSelectorCostMeanLHM)
                    iAU.resetLastMinCost()
                    //堆，也重置？
//                    iAU.heap=ArrayList()
                    env.strategyTuple.getRenEventSelectorFun = EnvObjHelper.getRenEventSelectorFunObj1()
                    iAU.nowCountOfNoBetter = 0
                    iAU.nowCountOfReset += 1
                    if (iAU.nowCountOfReset >= hAU.maxResets) {
                        debuggerList.pln(
                            "nowCountOfReset>=maxResets\t\t${iAU.nowCountOfReset}>=${hAU.maxResets}",
                            debugger_model_reset,
                        )
                        debuggerList.pln(
                            "minMean=${iAU.meanList.minOrNull()}",
                            debugger_model_reset,
                        )
                        return@iteration
                    }
                }
            }
        }
        debuggerList.pln(
            "iterations_end:",
            debugger_model_iterations,
        )

        debuggerList.pln(
            "nowCountOfReset=${iAU.nowCountOfReset}",
            debugger_model_reset,
        )

        renEventSelectorCostMeanLHMList.withIndex().map { (index: Int, lhm) ->
            if (lhm.size <= 0) return@map
            debuggerList.pln(
                "index=${index}",
                debugger_afterIterations_printCostMeanList,
            )
            val averages = lhm.map { (_, v) ->
                v
            }
            averages.map {
                //打印均值
                debuggerList.pln(
                    it.toString(),
                    debugger_afterIterations_printCostMeanList,
                )
            }
            drawLinesChart(
                yDataList = listOf(averages),
                savedFileName = "${FileRes.out_chart_file}" + "/LearningTest_t1" + "/t_${nowTimeStr}" + "/chart_" + "/chart${index}.png",
            )
        }

        val renEventSelectorCostMean2MinList = ArrayList<Double>()
        var if_break_renEventSelectorCostMean2LHMList = false
        renEventSelectorCostMeanLHMList.map {
            if (if_break_renEventSelectorCostMean2LHMList) return@map
            if (it.size <= 0) {
                if_break_renEventSelectorCostMean2LHMList = true
                return@map
            }
            val mean2Min = it.values.minOrNull()!!
            renEventSelectorCostMean2MinList.add(mean2Min)
        }
        drawLinesChart(
            yDataList = listOf(renEventSelectorCostMean2MinList),
            ifShowYDouble = false,
            savedFileName = "${FileRes.out_chart_file}" + "/LearningTest_t1" + "/t_${nowTimeStr}" + "/min" + "/chart_min.png",
        )
        require(renEventSelectorCostMean2MinList.size > 0)
        debuggerList.pln(
            "last_test",
            debugger_lastTest,
        )
        val getMinValueKey = renEventSelectorCostMean2MinList.getMinValueKey()!!
        debuggerList.pln(
            """
                getMinValueKey=${getMinValueKey}
                getMinValue=${renEventSelectorCostMean2MinList[getMinValueKey]}
            """.trimIndent(),
            debugger_lastTest,
        )
        env.strategyTuple.getRenEventSelectorFun = renEventSelectorCostMeanLHMList[getMinValueKey].getMinKey()!!
        val average = env.repeatRun2AndRecord(
            times = hAU.evalRuns * 10,
            debuggerList = debuggerList,
        ).toCostList().average()
        debuggerList.pln(
            "average=${average}",
            debugger_lastTest,
        )
    }

    @Test
    fun t1_debug() {
        t1(
            getDebugHyperArgUnit(),
            DSSUnit::getDistanceToMean,
            makeDebuggerList(
                debugger_2,
//                RunResult.debugger,
//                debugger_renEventSelectorCostListLHM,
                debugger_model_iterations,
                debugger_model_reset,
//                debugger_before_compare,
//                debugger_saveChart,
//                debugger_lastTest,
//                debugger_afterIterations_printCostMeanList,
            ),
        )
    }

    @Test
    fun t1t1() {
        t1(
            getType1HyperArgUnit(),
            DSSUnit::getDistanceToMean,
            makeDebuggerList(
                debugger_2,
                debugger_model_iterations,
                debugger_model_reset,
            ),
        )
    }

    @Test
    fun t1t2() {
        t1(
            getType1HyperArgUnit(),
            DSSUnit::getDistanceToCovarianceMatrix,
            makeDebuggerList(
                debugger_2,
            ),
        )
    }
}